package com.mrd.gtimp.business.service;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import javax.annotation.Resource;
import javax.transaction.Transactional;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.stereotype.Service;

import com.mrd.framework.utils.SpringUtils;
import com.mrd.gtimp.base.entity.Company;
import com.mrd.gtimp.business.entity.FundAccount;
import com.mrd.gtimp.business.entity.FundFlow;
import com.mrd.gtimp.v2.fund.FundFinancialTypeEnum;
import com.mrd.gtimp.v2.fund.FundIETypeEnum;
import com.mrd.gtimp.v2.fund.FundStateEnum;

/**
 * Created by dengdc on 2017/7/5.
 */
@Service
public class CommonPaymentBiz {
  private Logger logger = LoggerFactory.getLogger(CommonPaymentBiz.class);
  @Resource
  private JdbcTemplate jdbcTemplate;

  public static final Map<String, PaymentResultEnum> paymentResultMap =
      new HashMap<String, PaymentResultEnum>();

  public enum PaymentResultEnum {
    // 已收到到账通知，系统正在处理
    WAITING,
    // 已收到到账通知，支付成功
    SUCCESS,
    // 收到到账通知，支付失败
    FAIL
  }

  /**
   * 金额到账 1、插入流水记录 2、修改业务充值记录
   * 
   * @param compId 公司ID
   * @param orderNo 一线通订单号
   * @param fundType 支付方式
   * @param serialNum 外部流水号，列如农行的交易流水号，支付平台的订单号。
   */
  @Transactional
  public void arrivalAccount(String compId, double amount, String orderNo, FundIETypeEnum fundType,
      String serialNum) throws Exception {
    // 修改账户
    FundAccount fa = new FundAccount();
    FundFlow ff = new FundFlow();
    Company cp = new Company();
    cp.setId(compId);
    ff.setOwner(cp);
    ff.setFinancialType(FundFinancialTypeEnum.IN.toString());
    ff.setAmount(amount);
    changeFundAccBala(ff, fa);
    // 插入流水记录
    String flowId = String.valueOf(UUID.randomUUID());
    // java.sql.Date lastUpdDate = new java.sql.Date(fa.getLastUpdDate().getTime());
    jdbcTemplate.update(
        "insert into t_biz_fund_flow(id,com_id,amount,ie_type,FINANCIAL_TYPE,BALANCE,REG_DATE,BILL_NO,BANK_lIST_ID) values (?, ?, ?, ?, ?, ?, ?, ?, ?)",
        new PreparedStatementSetter() {
          public void setValues(PreparedStatement ps) throws SQLException {
            ps.setString(1, flowId);
            ps.setString(2, fa.getComId());
            ps.setDouble(3, amount);
            ps.setString(4, fundType.toString());
            ps.setString(5, FundFinancialTypeEnum.IN.toString());
            ps.setDouble(6, fa.getBalance());
            ps.setTimestamp(7, new java.sql.Timestamp(fa.getLastUpdDate().getTime()));
            ps.setString(8, orderNo);
            ps.setString(9, serialNum);
          }
        });
    // 修改业务记录
    jdbcTemplate.update(
        "update T_BIZ_FUND_AJUSTMENT set state=?,REVIEW_DATE=?,FUNDFLOW_ID=? where bill_no=?",
        new Object[] {FundStateEnum.SUCCESS.toString(), fa.getLastUpdDate(), flowId, orderNo});
  }

  /**
   * 改变账户余额
   * 
   * @param ff field compId and amount and FinancialType must be not null
   * @param fa can be null,return
   * @return
   */
  @Transactional
  public Boolean changeFundAccBala(FundFlow ff, FundAccount fa) throws Exception {
    boolean isSuccess = false;
    if (ff == null || ff.getOwner().getId() == null) {
      return isSuccess;
    }
    String compId = ff.getOwner().getId();
    JdbcTemplate jdbcTemplate = SpringUtils.getBean(JdbcTemplate.class);
    StringBuilder sql =
        new StringBuilder("select BALANCE,ACC_VERSION from T_BIZ_FUND_ACCOUNT where COM_ID=?");
    // 仅仅允许10次并发
    for (int i = 0; i < 10; i++) {
      Map<String, Object> acc =
          (Map<String, Object>) jdbcTemplate.queryForMap(sql.toString(), new Object[] {compId});
      // 取不到账户信息时，初始化它
      if (acc == null) {
        return isSuccess;
      }
      double bala = new Double(acc.get("BALANCE").toString());// 当前余额
      double amount = ff.getFinancialAmount();// 支出时为负数
      // 支出时余额判断
      if (ff.getFinancialType().equals(FundFinancialTypeEnum.OUT.toString())) {// 支出时
        if (bala + amount < 0) {// 当前余额不足
          break;
        }
      }
      Integer ver = new Integer(acc.get("ACC_VERSION").toString());// 当前版本
      StringBuilder updSql = new StringBuilder();
      // 修改余额
      bala = bala + amount;
      updSql.append(
          "update T_BIZ_FUND_ACCOUNT set BALANCE=?,ACC_VERSION=ACC_VERSION+1,LAST_UPD_DATE=? where COM_ID=? and ACC_VERSION=?");
      Date uptDate = new Date();
      int rows = jdbcTemplate.update(updSql.toString(), new Object[] {bala, uptDate, compId, ver});
      if (rows < 1) {
        logger.warn(compId + "账户并发操作,version=" + ver.toString());
      } else {
        isSuccess = true;
        logger.debug(compId + "账户余额修改成功,修改前version=" + ver.toString());
        // 构造账户信息
        if (fa == null)
          fa = new FundAccount();
        fa.setComId(compId);
        fa.setBalance(bala);
        fa.setLastUpdDate(uptDate);
        break;
      }

    } // end for

    return isSuccess;
  }

  /**
   * 根据订单号获取公司ID
   * 
   * @param orderNo 一线通充值订单号
   * @throws Exception 已定义内容异常
   */
  public String getComIdByOrderNo(String orderNo) throws Exception {
    // 根据orderno得到compId
    StringBuilder sql = new StringBuilder(
        "select COM_ID from T_BIZ_FUND_AJUSTMENT where BILL_NO=? and state!='SUCCESS'");
    String compId =
        (String) jdbcTemplate.queryForObject(sql.toString(), java.lang.String.class, orderNo);
    if (compId == null || "".equals(compId)) {
      logger.info(orderNo + "被重复通知,直接返回");
      throw new Exception(orderNo + "被重复通知");
    }
    return compId;
  }

  /**
   * 更新缓存
   * 
   * @param orderNo
   * @param paymentResultEnum
   */
  public String updatePaymentResult(String orderNo, PaymentResultEnum paymentResultEnum) {
    paymentResultMap.put(orderNo, paymentResultEnum);
    return paymentResultEnum.toString();
  }

  /**
   * 获取订单号对应的到账状态
   * 
   * @param orderNo 一线通订单号
   * @return WAITING 已经接到回执通知，并且正在处理 SUCCESS 接到回执通知，处理完成，支付结果为支付成功 FAIL 接到回执通知，处理完成，支付结果为支付失败
   */
  public String getPaymentResult(String orderNo) {
    PaymentResultEnum result = paymentResultMap.get(orderNo);
    if (result != null) {
      if (PaymentResultEnum.SUCCESS.equals(result)) {
        paymentResultMap.remove(orderNo);
      }
      return result.toString();
    } else {
      return null;
    }
  }

}
